$debugSwitch[onStuck] = false;
$debugSwitch[unBlock] = false;
$debugSwitch[checkForThreat] = false;
$debugSwitch[DoScan] = false;
$debugSwitch[openFire] = false;
$debugSwitch[ceaseFire] = false;
$debugSwitch[onTargetEnterLOS] = false;
$debugSwitch[onTargetExitLOS] = false;
$debugSwitch[spawn] = false;
$debugSwitch[Equip] = false;
$debugSwitch[GetTargetRange] = false;
$debugSwitch[getClosestEnemy] = false;
$debugSwitch[CreateBots] = false;
$debugSwitch[pickAISpawn] = false;
$debugSwitch[GetBearing] = false;
$debugSwitch[GetHeading] = false;
$debugSwitch[GetRelativeBearing] = false;
$debugSwitch[CheckArcOfSight] = false;
$debugSwitch[Misc] = false;

$ARC_OF_SIGHT = 120;

$MIN_SCAN_GAP = 2000;
$MAX_SCAN_GAP = 20000;
$MIN_TRIGGER_HOLD = 100;
$MAX_TRIGGER_HOLD = 200;
$MIN_ITCHY_FINGER = 2000;
$MAX_ITCHY_FINGER = 10000;
$MAX_THREAT_ENGAGE_RANGE = 100;
$MAX_AGGRESSIVENESS = 100;
$MAX_ATTENTION_LEVEL = 100;
$MAX_ALERTNESS = 100;
$STATIONARY = 0.0;

$cardinalDirection[0] = "0 10000 0";    // N
$cardinalDirection[1] = "6000 6000 0";  // NE
$cardinalDirection[2] = "10000 0 0";    // E
$cardinalDirection[3] = "6000 -6000 0"; // SE
$cardinalDirection[4] = "0 -10000 0";   // S
$cardinalDirection[5] = "-6000 -6000 0";// SW
$cardinalDirection[6] = "-6000 0 0";    // W
$cardinalDirection[7] = "-6000 6000 0"; // NW
$NUM_CARDINALS = 8;


datablock PlayerData(AIGuardDB : PlayerBody)
{
   //Use Armor so our NPC will take damage
   className = Armor;
   maxInv[CrossbowAmmo] = 500000;
   maxInv[RocketLauncherAmmo] = 500000;
   shapeFile = "~/data/shapes/player/player.dts";
};

function AIGuardDB::checkForThreat(%this,%obj)
{
  DebugPrint( "%this:"@%this@"~AIGuardDB::checkForThreat  (from:"@%obj@")", "checkForThreat");
  
	// this code used to make the guard fire
	// modified by Maxim from Finney text on 3/12/06
   if(!isObject(%obj))
    return;

   %idx = %obj.getClosestEnemy();
   
   if (%idx < 0)
     return 0;
  
   %target = ClientGroup.getObject( %idx );

   if ( !%obj.CheckArcOfSight(%target.player) ) 
     %testRange = %obj.range / 2;
   else
     return;

   if ( %obj.GetTargetRange(%target.player) <  %testRange)
    {
       return %target.player;
    }
 
  DebugPrint( "no threat (from:"@%obj@")", "checkForThreat");
  return 0;
}

function AIGuardDB::DoScan(%this,%obj)
{
  DebugPrint("%this:"@%this@"~AIGuardDB::DoScan (from:"@%obj@")", "DoScan");
  if(!isObject(%obj))
    return;
  if (%obj.getState() $= "Dead")
    return;
  cancel(%this.scheduledCheck);
  if (%obj.attentionLevel<=0)  // if attentionLevel is non-zero, keep looking in same direction
    %obj.attentionLevel=0;
  else
    %obj.attentionLevel--;
  if (%obj.attentionLevel==0)  // if attentionLevel is non-zero, keep looking in same direction
  {
   %look = getRandom($NUM_CARDINALS-1);
   if (%this.look != %look)
      %this.look = %look;
   else
   {
      %this.look = %look + 1;
      if (%this.look < $NUM_CARDINALS-1)
         %this.look++;
      else
         %this.look = 0;
   }
  }
  %obj.setAimLocation($cardinalDirection[%this.look]);
  if ( (%tgtPlayer = %this.checkForThreat(%obj)) != 0)
  {
    if (%obj.currentTarget)
    {
       if (%obj.currentTarget==%tgtPlayer)
       {
                DebugPrint( "STILL A THREAT (from:"@%obj@")", "DoScan");
          %obj.setAimObject( %tgtPlayer, "0 0 1" );
          %obj.attentionLevel = %obj.attention;
       }
       else
       {
               DebugPrint( "CHANGED THREAT (from:"@%obj@")", "DoScan");
          %obj.currentTarget =  %tgtPlayer;
          %obj.setAimObject( %obj.currentTarget, "0 0 1" );
       }
     }
     else
     {
            DebugPrint( "NEW THREAT!! (from:"@%obj@")", "DoScan");
       %obj.setAimObject( %tgtPlayer, "0 0 1" );
       %obj.currentTarget =  %tgtPlayer;
       %obj.attentionLevel = %obj.attention;
     }
  }
  else
  {
    if (%obj.getAimObject)
    {
       %obj.clearAim();
            DebugPrint( "> %obj.clearAim (from:"@%obj@")", "DoScan");
       %obj.currentTarget = 0; // forget this target
    }
    %this.nextScan = %this.schedule($MIN_SCAN_GAP+getRandom($MAX_SCAN_GAP/%this.alertness), "doScan", %obj);
  }
}

function AIGuardDB::onTargetEnterLOS(%this,%obj)
{
   // If an aim target object is set, this method is invoked when
   // that object becomes visible.
      DebugPrint( "%this:"@%this@"~AIGuardDB::onTargetEnterLOS LOS TARGET ! (from:"@%obj@")", "onTargetEnterLOS");
   if(!isObject(%obj))
      return;
   %obj.attentionLevel = %this.attention;
   %this.schedule($MIN_ITCHY_FINGER+getRandom($MAX_ITCHY_FINGER), "pauseFire", %obj);
   %this.schedule($MIN_SCAN_GAP+getRandom($MAX_SCAN_GAP/%this.alertness), "doScan", %obj);
   %obj.setImageTrigger(0,true);
}

function AIGuardDB::onTargetExitLOS(%this,%obj)
{
   // If an aim target object is set, this method is invoked when
   // the object is no longer visible.
   DebugPrint( "%this:"@%this@"~AIGuardDB::onTargetExitLOS Fuhgetaboutit (from:"@%obj@")", onTargetExitLOS);
   if(!isObject(%obj))
      return;
   %obj.setImageTrigger(0,false);
   %obj.clearAim();
      DebugPrint( "> %obj.clearAim (from:"@%obj@")", "onTargetExitLOS");
   %obj.currentTarget = 0;  // forget this target
   %this.schedule($MIN_SCAN_GAP, "doScan", %obj);
}


function AIGuardDB::pauseFire(%this,%obj)
{
      DebugPrint("%this:"@%this@"~AIGuardDB::ceaseFire (from:"@%obj@")", pauseFire);
   if(!isObject(%obj))
      return;
   %obj.setImageTrigger(0,false);
   %this.schedule($MIN_TRIGGER_HOLD+getRandom($MAX_TRIGGER_HOLD), "ceaseFire", %obj);
}

function AIGuardDB::ceaseFire(%this,%obj)
{
      DebugPrint( "%this:"@%this@"~AIGuardDB::ceaseFire (from:"@%obj@")", ceaseFire);
   if(!isObject(%obj))
      return;
   %obj.setImageTrigger(0,false);
   %obj.clearAim();
   %obj.currentTarget = 0; // forget this target
   %this.schedule($MIN_SCAN_GAP, "doScan", %obj);
}

function spawnbot(%index,%role)
{
  DebugPrint( "%index:"@%index@"%role:"@%role@"~", spawnbot);
  %me = new AIPlayer() {
    dataBlock = AIGuardDB;
    aiPlayer = true;
  };
  MissionCleanup.add(%me);
  AIGroup.add(%me);
  
  %me.setSkinName("jinroh");
  
  %me.setAimObject( 0 );
  %me.look = 0;
  %spawn=pickAISpawn(%index,%role);
  //%me.range = 100;
  //%me.range     = %spawn.range      < $MAX_THREAT_ENGAGE_RANGE ? %spawn.range              :  $MAX_THREAT_ENGAGE_RANGE;
  //%me.attention = %spawn.attention  < $MAX_ATTENTION_LEVEL     ? %spawn.attention/5        :  $MAX_ATTENTION_LEVEL/5;
  //%me.alertness = %spawn.alertness  < $MAX_ALERTNESS           ? %spawn.alertness/10       :  $MAX_ALERTNESS/10;
  //%me.aggression   = %spawn.aggressiveness < $MAX_AGGRESSIVENESS  ? %spawn.aggressiveness * 0.015 :  $MAX_AGGRESSIVENESS * 0.015;
  %me.range = 60;
  %me.attention = 20;
  %me.alertness = 1000;
  //%me.aggression   = 10;
  %me.attentionLevel = 0;
  
  %me.index = %index;
  %me.setTransform(%spawn.getTransform());
  //%me.setEnergyLevel(60);
  %me.role = %role;
  %me.setShapeName(%me.getName() SPC %me.role);
  %me.nextBlockCheck = 0;
  %me.conformToGround = 0;
  %me.Equip(CrossBow,CrossBowAmmo);
  %me.setMoveSpeed($STATIONARY);
  
  // Supersize the boss man
  if (%me.role $= "Boss")
  {
     %me.range = 65;
     %me.setScale("1.5 1.5 1.5");
	 %me.Equip(RocketLauncher, RocketLauncherAmmo);
  }
  
  echo("Added [" SPC %me SPC "] :" SPC %me.role SPC "#" SPC  %me.index );

  %me.setAimLocation( $cardinalDirection[%me.look]);
  %me.getDataBlock().schedule(2000, "doScan", %me);
  return %me;
}

function AIPlayer::Equip(%this,%weaponDBName,%ammoDBName)
{
   DebugPrint( "%this:"@%this@"~AIPlayer::Equip", Equip);
   %weapon = new Item() {
      dataBlock = %weaponDBName;
   };
   %ammo = new Item() {
      dataBlock = %ammoDBName;
   };
   DebugPrint("weapon:"@%weaponDBName, "Equip");
   DebugPrint("ammo:"@%ammoDBName, "Equip");
   MissionCleanup.add(%weapon);
   MissionCleanup.add(%ammo);
   %weaponImageName = %weaponDBName @"Image";
  %this.mountImage(%weaponImageName,0);
  %this.setInventory(%ammoDBName,1000);
  %this.use(%weaponDBName);
}

function AIPlayer::GetTargetRange(%this, %target)
{
   DebugPrint( "%this:"@%this@"~AIPlayer::GetTargetRange", GetTargetRange);
   %tgtPos = %target.getPosition();
   %eyePoint = %this.getWorldBoxCenter();
   %distance = VectorDist(%tgtPos, %eyePoint);
      DebugPrint("Actual range to target: " @ %distance , GetTargetRange);
   return %distance;
}

function AIPlayer::getClosestEnemy(%this) 
{
   DebugPrint( "%this:"@%this@"~AIPlayer::getClosestEnemy", getClosestEnemy);

   %index = -1;
   %botPos = %this.getPosition();
   %count = ClientGroup.getCount();
   for(%i = 0; %i < %count; %i++)
   {
      %client = ClientGroup.getObject(%i);
      if (%client.player $= "" || %client.player == 0 )
         return -1;
      %playPos = %client.player.getPosition();

      %tempDist = VectorDist(%playPos, %botPos);
      if(%i == 0) 
      {
         %distance = %tempDist;
         %index = %i;
      }
      else 
      {
         if(%distance > %tempDist) 
         {
            %distance = %tempDist;
            %index = %i;
         }
      }
   }
   return %index;
}

function CreateBots()
{
   new SimSet (AIGroup);
   warn("# # # # # # # # # # # # Creating NPC characters # # # # # # # # # # # # ");
   
   // Create guards
   if ( (%role=nameToID("MissionGroup/AIDropPoints/Guard")) >= 0 )
   {
      %count=%role.getCount();
      for ( %i = 0; %i < %count; %i++)
      {
         spawnbot(%i,"Guard");
      }
   }
   
   // Create boss
   if ( (%role=nameToID("MissionGroup/AIDropPoints/Boss")) >= 0 )
   {
      %count=%role.getCount();
      for ( %i = 0; %i < %count; %i++)
      {
         spawnbot(%i,"Boss");
      }
   }
   
   toggleNames(1);
}

function pickAISpawn(%index,%role)
{
   %groupName = "MissionGroup/AIDropPoints/" @ %role;
   %group = nameToID(%groupName);

   if (%group != -1) 
   {
      %count = %group.getCount();
      if (%count != 0) 
      {
        // %index = getRandom(%count-1);
         %spawn = %group.getObject(%index);
         return %spawn;
      }
      else
            DebugPrint("No spawn points found in " @ %groupName, "pickAISpawn");
   }
   else
       DebugPrint("Missing spawn points group " @ %groupName, "pickAISpawn");

   return %spawn;
}

//  Return the bearing angle of a target's position (%there)
//  from an object's position (%here)
function AIPlayer::GetBearing(%this, %that)
{
  DebugPrint("%this:"@%this@"~AIPlayer::GetBearing",GetBearing,AI_Whisper);
  %here = %this.getPosition();
  %there = %that.getPosition();
  %xHere = getWord(%here,0);
  %yHere = getWord(%here,1);
  DebugPrint("xhere:"@%xHere SPC "yHere"@%yHere,GetBearing,AI_Whisper);
  %xThere = getWord(%there,0);
  %yThere = getWord(%there,1);
  DebugPrint("xThere:"@%xThere SPC "yThere"@%yThere,GetBearing,AI_Whisper);
  
  %x = %xThere - %xHere;
  DebugPrint("x:"@%x,AI_Whisper);
  %y = %yThere - %yHere;
  DebugPrint("y:"@%y,AI_Whisper);
  if (%x!=0 )
  {
    %slope = %y/%x;
     DebugPrint("slope:"@%slope,GetBearing,AI_Whisper);
    %angle=mRadToDeg(mATan(%slope,-1) );//-1 is only 2nd arg that works
    DebugPrint("Angle in:" SPC %angle,GetBearing,AI_WhisperLoud);
  }
  else
  {
    DebugPrint("vertical",GetBearing,AI_Whisper);
    %angle = 90.000;
  }
  if( (%x>=0) && (%y>=0) )    // target in quadrant 1, 0-89.999 degrees
    %adjustment = -90;
  else if( (%x>=0) && (%y<0) )  //quadrant 2,  90-179.999 degrees
    %adjustment = 270;
  else if( (%x<0) && (%y<0) ) // quadrant 3, 180-269.999 degrees
    %adjustment = 90;
  else                              //quadrant 4, 270-359.999 degrees
    %adjustment = 90+360;
  %angle += %adjustment;
  return %angle;
}

function AIPlayer::GetHeading(%this)
{
  DebugPrint("%this:"@%this@"~AIPlayer::GetHeading",GetHeading,AI_Whisper);
  %hdg = GetWord( %this.rotation,3);
  if (GetWord( %this.rotation,2)$="-1")
    %hdg = 360-%hdg;
  return %hdg;
}

function AIPlayer::GetRelativeBearing(%this,%that)
{
  DebugPrint("%this:"@%this@"~AIPlayer::GetRelativeBearing",GetRelativeBearing,AI_Whisper);
  %azimuth = %this.GetBearing(%that);
  DebugPrint("ANGLE:"@%azimuth,AI_Alert);
  
  %heading = %this.GetHeading();
  DebugPrint("HEADING:"@%heading,GetRelativeBearing,AI_Alert);
  if (%heading >= %azimuth)
  {
    %bearing = %heading - %azimuth;
    %bearing *= -1;
  }
  else
  {
    %bearing = %azimuth - %heading;
  }
  DebugPrint("BEARING:"@%bearing,GetRelativeBearing,AI_Alert);
  return %bearing;
}

function AIPlayer::CheckArcOfSight(%this,%that)
{
  DebugPrint("%this:"@%this@"~AIPlayer::CheckArcOfSight",CheckArcOfSight,AI_Whisper);
  DebugPrint("%that:"@%that,GetRelativeBearing,AI_Whisper);
  %relbearing = %this.GetRelativeBearing(%this,%that);
  DebugPrint("relbearing:"@%relbearing,CheckArcOfSight,AI_Alert);
  if ( (%relbearing > -($ARC_OF_SIGHT/2)) && (%relbearing < -($ARC_OF_SIGHT/2)) ) 
    %result = true;
  else
    %result = false;
  DebugPrint("result:"@%result,CheckArcOfSight,AI_Alert);
  return %result;
}

function showDebug(%which)
{
  echo($debugSwitch[%which]);
}
function showAllDebug()
{
    echo("onStuck:"@$debugSwitch[onStuck]);
    echo("unBlock:"@$debugSwitch[unBlock]);
    echo("checkForThreat:"@$debugSwitch[checkForThreat]);
    echo("DoScan:"@$debugSwitch[DoScan]);
    echo("openFire:"@$debugSwitch[openFire]);
    echo("ceaseFire:"@$debugSwitch[ceaseFire]);
    echo("onTargetEnterLOS:"@$debugSwitch[onTargetEnterLOS]);
    echo("onTargetExitLOS:"@$debugSwitch[onTargetExitLOS]);
    echo("spawn:"@$debugSwitch[spawn]);
    echo("Equip:"@$debugSwitch[Equip]);
    echo("GetTargetRange:"@$debugSwitch[GetTargetRange]);
    echo("getClosestEnemy:"@$debugSwitch[getClosestEnemy]);
    echo("CreateBots:"@$debugSwitch[CreateBots]);
    echo("pickAISpawn:"@$debugSwitch[pickAISpawn]);
    echo("GetBearing:"@$debugSwitch[GetBearing]);
    echo("GetHeading:"@$debugSwitch[GetHeading]);
    echo("GetRelativeBearing:"@$debugSwitch[GetRelativeBearing]);
    echo("CheckArcOfSight:"@$debugSwitch[CheckArcOfSight]);
    echo("Misc:"@$debugSwitch[Misc]);
}
function clearAllDebug()
{
$debugSwitch[onStuck] = false;
$debugSwitch[unBlock] = false;
$debugSwitch[checkForThreat] = false;
$debugSwitch[DoScan] = false;
$debugSwitch[openFire] = false;
$debugSwitch[ceaseFire] = false;
$debugSwitch[onTargetEnterLOS] = false;
$debugSwitch[onTargetExitLOS] = false;
$debugSwitch[spawn] = false;
$debugSwitch[Equip] = false;
$debugSwitch[GetTargetRange] = false;
$debugSwitch[getClosestEnemy] = false;
$debugSwitch[CreateBots] = false;
$debugSwitch[pickAISpawn] = false;
$debugSwitch[GetBearing] = false;
$debugSwitch[GetHeading] = false;
$debugSwitch[GetRelativeBearing] = false;
$debugSwitch[CheckArcOfSight] = false;
$debugSwitch[Misc] = false;
}
function setAllDebug()
{
$debugSwitch[onStuck] = true;
$debugSwitch[unBlock] = true;
$debugSwitch[checkForThreat] = true;
$debugSwitch[DoScan] = true;
$debugSwitch[openFire] = true;
$debugSwitch[ceaseFire] = true;
$debugSwitch[onTargetEnterLOS] = true;
$debugSwitch[onTargetExitLOS] = true;
$debugSwitch[spawn] = true;
$debugSwitch[Equip] = true;
$debugSwitch[GetTargetRange] = true;
$debugSwitch[getClosestEnemy] = true;
$debugSwitch[CreateBots] = true;
$debugSwitch[pickAISpawn] = true;
$debugSwitch[GetBearing] = true;
$debugSwitch[GetHeading] = true;
$debugSwitch[GetRelativeBearing] = true;
$debugSwitch[CheckArcOfSight] = true;
$debugSwitch[Misc] = true;
}

function DebugPrint(%theMsg,%facility,%priority)
{
  if ($debugSwitch[%facility])
  {
    %theMsg = %priority@"~~~"@%theMsg;
    if (%priority $= "")
    {
      echo(%theMsg);
    }
    else
    {
      switch$ (%priority)
      {
        case "AI_Panic":
          error("~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~");
          error("~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~");
          error(%theMsg);
          error("~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~");
          error("~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~");
        case "AI_Alert":
          error(%theMsg);
        case "AI_AlertLoud":
          error("~~~~~~~~~~~~~~" SPC %theMsg SPC "~~~~~~~~~~~~~~");
        case "AI_Normal":
          echo(%theMsg);
        case "AI_NormalLoud":
          echo("~~~~~~~~~~~~~~" SPC %theMsg SPC "~~~~~~~~~~~~~~");
        case "AI_Whisper":
          warn(%theMsg);
        case "AI_WhisperLoud":
          warn("~~~~~~~~~~~~~~" SPC %theMsg SPC "~~~~~~~~~~~~~~");
        default:
          echo(%theMsg);
      }
    }
  }
}